#pragma once 
#include "Util.hpp"
#include "Log.hpp"
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <wait.h>
#include <sys/types.h>
#include <sys/sendfile.h>
#include <sys/socket.h>
#include <sys/stat.h>

#define SEP       ": "
#define OK        200
#define NOT_FIND  404
#define WEB_ROOT  "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"

//class Code2Desc{
//  private:
//    std::unordered_map<int, std::string> _code2_desc;
//  public:
//    Code2Desc();
//    void InitCode2Desc(){
//      _code2_desc.insert({200, "OK"});
//    }
//    ~Code2Desc();
//};

static std::string Code2Desc(int code){
  std::string desc;
  switch(code){
    case 200: desc = "OK"; break; 
    case 404: desc = "NOT_FIND"; break;
    default: break;
  }
  return desc;
}

static std::string Suffix2Desc(const std::string& suffix){
  std::string desc;
  static std::unordered_map<std::string, std::string> suffix2desc = {
    {".html", "text/html"}, {".css", "text/css"},
    {".js",  "application/x-javascript"}, {".jpg", "application/x-jpg"},
    {".xml", "application/xml"}
  };
  //return suffix2desc[suffix];
  auto iter = suffix2desc.find(suffix);
  if (iter != suffix2desc.end()){
    return iter->second;
  }
  else{
    return "text/html";
  }
}

class HttpRequest{
  public:
    std::unordered_map<std::string, std::string> _header_kv;

    std::string _request_line;
    std::vector<std::string> _request_header;
    std::string _blank;
    std::string _request_body;

    //解析完毕的结果
    std::string _method;  //请求方法
    std::string _uri;    //请求资源 path?args
    std::string _version;//请求版本
    int _content_length;
    std::string _path;
    std::string _suffix;
    std::string _query_string;

    bool _cgi;
  public:
    HttpRequest():_content_length(0), _cgi(0){};
    ~HttpRequest(){};
};


class HttpResponse{
  private:
  public:
    std::string _status_line;
    std::vector<std::string> _response_header;
    std::string _blank;
    std::string _response_body;

    int _status_code;
    int _fd;
    int _body_size;
  public:
    HttpResponse():_blank(LINE_END), _status_code(OK), _fd(-1), _body_size(0){}
    ~HttpResponse(){};
};


//读取请求，分析请求，构建相应
//IO通信
class EndPoint{
  private:
    void RecvHttpRequestLine();
    void RecvHttpRequestHeader();
    bool IsNeedRecvHttpRequestBody();
    void RecvHttpRequestBody();

    int ProcessNonCgi(int size);
    int ProcessCgi();

    void ParseHttpRequestLine();
    void ParseHttpRequestHeader();

  public:
    EndPoint(int sock) :_sock(sock){}
    void RecvHttpRequest();
    void BuildHttpResponse();
    void SendHttpResponse();
    ~EndPoint() {close(_sock);}
  private:
    int _sock;
    HttpRequest  _http_request;
    HttpResponse _http_response;

    //状态码和状态码描述映射表
    
};

void EndPoint::RecvHttpRequestLine(){
  size_t size = Util::ReadLine(_sock, _http_request._request_line);
  _http_request._request_line.resize(size - 1);
  LOG(INFO, _http_request._request_line);
  //test
  //copy.resize(copy.size() - 1);
  //LOG(INFO, copy);
}

void EndPoint::RecvHttpRequestHeader(){
  std::string line;
  std::string copy;
  while (line != "\n"){
    line.clear();
    copy.clear();
    Util::ReadLine(_sock, line);
    copy = line;
    copy.resize(copy.size() - 1);
    if (copy.size() == 0){
      break;
    }
    _http_request._request_header.push_back(copy);
    //test
    std::string copy = line;
    copy.resize(copy.size() - 1);
    LOG(INFO, copy);
  }
  if (line == "\n"){
    _http_request._blank = line;
  }
}

bool EndPoint::IsNeedRecvHttpRequestBody(){
  const auto& method = _http_request._method;
  if (method == "POST"){
    const auto& header_kv = _http_request._header_kv;
    auto iter = header_kv.find("Content-Length");
    if (iter != header_kv.end()){
      _http_request._content_length = atoi(iter->second.c_str());
      LOG(INFO, "Need recv httprequest body");
      return true;
    }
  }
  return false;
}

void EndPoint::RecvHttpRequestBody(){
  if (IsNeedRecvHttpRequestBody()){
    LOG(INFO, "begin recv request body");
    int content_length = _http_request._content_length;
    auto& body = _http_request._request_body;
    char ch = 0;
    std::cout << "content_length = " << content_length << std::endl;
    while (content_length--){
      ssize_t size = recv(_sock, &ch, 1, 0);
      if (size > 0){
        body.push_back(ch);
      }
      else{
        break;
      }
    }
    LOG(INFO, body);
  }
}

void EndPoint::RecvHttpRequest(){
    RecvHttpRequestLine();
    RecvHttpRequestHeader();
    ParseHttpRequestLine();
    ParseHttpRequestHeader();
    RecvHttpRequestBody();
}


void EndPoint::ParseHttpRequestLine(){ 
  auto& line = _http_request._request_line;
  std::stringstream ss(line);
  ss >> _http_request._method >> _http_request._uri >> _http_request._version;
  auto& method = _http_request._method;
  std::transform(method.begin(), method.end(), method.begin(), ::toupper);
  LOG(INFO, _http_request._method);
  LOG(INFO, _http_request._uri);
  LOG(INFO, _http_request._version);
}

void EndPoint::ParseHttpRequestHeader(){
  for (auto &iter : _http_request._request_header){
    std::string key; 
    std::string value;
    if (Util::CutString(iter, key, value, SEP)){
      std::cout << "debug :" << key << std::endl;
      std::cout << "debug :" << value << std::endl;
      _http_request._header_kv.insert(make_pair(key, value));
    }
    else{
      LOG(ERRNO, iter);
    }
  }
}

int EndPoint::ProcessNonCgi(int size){
  _http_response._fd = open(_http_request._path.c_str(), O_RDONLY);
  _http_response._status_code = OK;
  if (_http_response._fd > 0){
    _http_response._status_line = HTTP_VERSION;
    _http_response._status_line += " ";
    _http_response._status_line += std::to_string(_http_response._status_code);
    _http_response._status_line += " ";
    _http_response._status_line += Code2Desc(_http_response._status_code);
    _http_response._status_line += LINE_END;

    std::string header_line = "Content-Type: ";
    header_line += Suffix2Desc(_http_request._suffix);
    header_line += LINE_END;
    _http_response._response_header.push_back(header_line);

    header_line = "Content-Length: ";
    header_line += std::to_string(size);
    header_line += LINE_END;
    _http_response._response_header.push_back(header_line);

    _http_response._body_size = size;
    return OK;
  }
  return NOT_FIND;
}

int EndPoint::ProcessCgi(){
  LOG(INFO, "process cgi mthod");
  //std::cout << "debug Cgi function" << std::endl;
  //创建子线程去执行Cig程序，主线程自始至终不可以改变,因为httpserver是服务器
  int input[2];
  int output[2];
  int code = OK;

  if (pipe(input) < 0){
    code = NOT_FIND;
    LOG(ERRNO, "pipe create failed");
    return NOT_FIND;
  }
  if (pipe(output) < 0){
    code = NOT_FIND;
    LOG(ERRNO, "pipe create failed");
    return NOT_FIND;
  }

  const auto& bin = _http_request._path;
  auto& method = _http_request._method;
  auto& body_text = _http_request._request_body;
  auto& query_string = _http_request._query_string;
  std::cout << "method = " << method << std::endl;
  std::cout << "bin" << bin << std::endl;
  //std::cout << "_http_request._query_string = " << _http_request._query_string << std::endl;
  std::string query_string_env;
  std::string method_env = "METHOD_ENV=";
  method_env += method;
  putenv((char*)method_env.c_str());
  if (method == "GET"){
    query_string_env = "QUERY_STRING=";
    query_string_env += query_string;
    putenv((char*)query_string_env.c_str());
  }
  pid_t pid = fork();
  if (pid == 0){
    //子线程 执行Cig程序
    close(input[0]);
    close(output[1]);
    LOG(INFO, "create child process success");
    //关闭标准输入输出/改为读和写
    dup2(output[0], 0);
    dup2(input[1], 1);
    execl(bin.c_str(), bin.c_str(), nullptr);
    exit(1);
  }
  else if(pid < 0){
    LOG(ERRNO, "fock errno");
    return NOT_FIND;
  }
  else{
    //父进程接收子线程结果
    close(input[1]);
    close(output[0]);

     if (method == "POST"){
      int size = 0; //每次write的字节数
      int cur = 0;  //已经写入的字节数
      while ((size = write(output[1], body_text.c_str() + cur, body_text.size() - cur)) > 0){
        cur += size;
      }
    }
    waitpid(pid, nullptr, 0);
    close(input[0]);
    close(output[1]);
  }
  
  return OK;
}

void EndPoint::BuildHttpResponse(){
  std::string path;
  std::string method = _http_request._method;
  int size = 0;
  size_t found;
  auto& code = _http_response._status_code;
  //std::cout << method << std::endl;
  if (method != "GET" && method != "POST"){
    //非法请求
    LOG(WARNING, "method is not right");
    code = NOT_FIND;
    goto END;
  }
  if (method == "GET"){
    size_t pos = _http_request._uri.find('?');
    if (pos != std::string::npos){
      Util::CutString(_http_request._uri, _http_request._path, _http_request._query_string, "?");
      _http_request._cgi =  true;
      //1.路径是否合法 2.资源是否存在
    }
    else{
      _http_request._path = _http_request._uri;
    }
    //std::cout << "debug uri" << _http_request._uri << std::endl;
    //std::cout << "debug path" << _http_request._path << std::endl;
    //std::cout << "debug query_string" << _http_request._query_string << std::endl;
  }
  else if(_http_request._method == "POST"){
    //POST 
    _http_request._cgi = true;
    _http_request._path = _http_request._uri;
  }
  path = _http_request._path;
  LOG(INFO, path);  
  _http_request._path = WEB_ROOT;
  _http_request._path += path;
  LOG(INFO, _http_request._path);
  //std::cout << "debug path" << _http_request._path << std::endl;
  if (_http_request._path[_http_request._path.size() - 1] == '/'){
    _http_request._path += HOME_PAGE;
  }
  //std::cout << "debug " << _http_request._path << std::endl;
  struct stat st;
  if (stat(_http_request._path.c_str(), &st) == 0){
    //资源存在
    if (S_ISDIR(st.st_mode)){
      //说明请求的资源是一个目录，是不被允许的，需要进行默认处理
      //虽然是一个目录，但是绝对不会以/结尾
      _http_request._path += HOME_PAGE;
      stat(_http_request._path.c_str(), &st);
    }
    if ((st.st_mode & S_IXUSR) & (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)){
      //请求的是一个可执行程序，需要特殊处理
      LOG(INFO, ".exe spaciel chuli");
      _http_request._cgi = true;
    }
    size = st.st_size;
  }
  else {
    //资源不存在
    std::string info = _http_request._path; 
    info += "Not Find";
    LOG(WARNING, info);
    _http_response._status_code = NOT_FIND;
    goto END;
  }

  //获取path suffix
  found = _http_request._path.rfind(".");
  if (found == std::string::npos){
    _http_request._suffix = ".html";
  }
  else {
    _http_request._suffix = _http_request._path.substr(found);
  }
  

  if (_http_request._cgi){
    int code = ProcessCgi();
  }
  else{
    //1.目标网页一定存在 2.返回并非网页内容，而是构建HTTP响应
    _http_response._status_code = ProcessNonCgi(size);   //简单文本网页返回
  }
END:
  if (_http_response._status_code != OK){
  }
  return;
}

void EndPoint::SendHttpResponse(){
  send(_sock, _http_response._status_line.c_str(), _http_response._status_line.size(), 0);
  for (auto& iter : _http_response._response_header){
    send(_sock, iter.c_str(), iter.size(), 0);
  }
  send(_sock, _http_response._blank.c_str(), _http_response._blank.size(), 0);
  sendfile(_sock, _http_response._fd, nullptr, _http_response._body_size);
  close(_http_response._fd);
}





class Entrance{
  public:
    static void* HandlerRequest(void* args){
      LOG(INFO, "handler request begin");
      int sock = *(int*)args;
      delete (int*)args;
      //std::cout << "get a new link" << sock << std::endl;

#ifdef DEBUG 
      char buffer[1024 * 10] = {0};
      std::cout << "\n\n----------------------"<<std::endl;
      recv(sock, buffer, sizeof(buffer) - 1, 0);
      std::cout << buffer << std::endl;
      std::cout << "\n\n----------------------"<<std::endl;
#else 
      EndPoint *ep = new EndPoint(sock);
      ep->RecvHttpRequest();
      ep->BuildHttpResponse();
      ep->SendHttpResponse();
      delete ep;
#endif
      //std::string line;
      //Util::ReadLine(sock, line);
      //std::cout << line << std::endl;
      close(sock);
      LOG(INFO, "handler request end");
      return nullptr;
    }
};



